#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <time.h>

// 关键参数设置 - 学生可以修改这些值进行实验
#define BUFFER_SIZE 5       // 缓冲区大小
#define ITEMS_PER_PRODUCER 3  // 每个生产者生产的产品数
#define ITEMS_PER_CONSUMER 5  // 每个消费者消费的产品数
#define NUM_PRODUCERS 1     // 生产者数量
#define NUM_CONSUMERS 1     // 消费者数量

// 共享内存结构
struct shared_memory {
    int buffer[BUFFER_SIZE];  // 循环缓冲区
    int in;                   // 生产位置
    int out;                  // 消费位置
    int count;                // 缓冲区中当前的产品数量
    int total_produced;       // 已生产项目计数
    int total_consumed;       // 已消费项目计数
};

// 信号量标识符
int empty_sem;   // 空位信号量
int full_sem;    // 满位信号量
int mutex_sem;   // 互斥信号量

// 共享内存标识符
int shmid;
struct shared_memory *shm_ptr;

// 函数原型
void initialize_synchronization();
void cleanup_synchronization();
void producer_process(int id);
void consumer_process(int id);
void print_buffer_status();

// P操作原语 - 等待资源
void sem_p(int sem_id) {
    // TODO: 实现P操作原语(wait)
    // 提示: 使用semop系统调用减少信号量值
    struct sembuf sb;
    sb.sem_num = 0;
    sb.sem_op = -1;
    sb.sem_flg = 0;
    if (semop(sem_id, &sb, 1) == -1) {
	perror("sem_p失败");
	exit(1);
    }
}

// V操作原语 - 释放资源
void sem_v(int sem_id) {
    // TODO: 实现V操作原语(signal)
    // 提示: 使用semop系统调用增加信号量值
    struct sembuf sb;
    sb.sem_num = 0;
    sb.sem_op = 1;
    sb.sem_flg = 0;
    if (semop(sem_id, &sb, 1) == -1) {
	perror("sem_v失败");
	exit(1);
    }
}

int main(int argc, char *argv[]) {
    pid_t pid;
    int i;

    printf("生产者消费者问题 (多进程版本)\n");
    printf("参数设置: 缓冲区大小=%d, 生产者=%d, 消费者=%d\n", 
           BUFFER_SIZE, NUM_PRODUCERS, NUM_CONSUMERS);
    printf("每个生产者生产%d个产品, 每个消费者消费%d个产品\n",
           ITEMS_PER_PRODUCER, ITEMS_PER_CONSUMER);
    printf("总共需生产: %d, 总共需消费: %d\n",
           NUM_PRODUCERS * ITEMS_PER_PRODUCER, NUM_CONSUMERS * ITEMS_PER_CONSUMER);

    // 初始化随机数生成器
    srand(time(NULL));

    // 初始化信号量和共享内存
    initialize_synchronization();

    // 创建生产者进程
    for (i = 0; i < NUM_PRODUCERS; i++) {
        pid = fork();
        
        if (pid == 0) {
            // 子进程作为生产者
            producer_process(i + 1);
            exit(0);  // 子进程完成后退出
        }
    }
    
    // 创建消费者进程
    for (i = 0; i < NUM_CONSUMERS; i++) {
        pid = fork();
        
        if (pid == 0) {
            // 子进程作为消费者
            consumer_process(i + 1);
            exit(0);  // 子进程完成后退出
        }
    }
    
    // 父进程等待所有子进程完成
    for (i = 0; i < NUM_PRODUCERS + NUM_CONSUMERS; i++) {
        wait(NULL);
    }
    
    // 显示结果
    printf("\n========== 结果汇总 ==========\n");
    printf("总共生产: %d 个产品\n", shm_ptr->total_produced);
    printf("总共消费: %d 个产品\n", shm_ptr->total_consumed);
    
    // 清理资源
    cleanup_synchronization();
    
    printf("所有进程已成功完成！\n");
    return 0;
}

// 初始化信号量和共享内存
void initialize_synchronization() {
    // 创建共享内存
    shmid = shmget(IPC_PRIVATE, sizeof(struct shared_memory), IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget 失败");
        exit(1);
    }
    
    // 附加共享内存
    shm_ptr = (struct shared_memory *)shmat(shmid, NULL, 0);
    if (shm_ptr == (struct shared_memory *)-1) {
        perror("shmat 失败");
        exit(1);
    }
    
    // 初始化共享内存
    shm_ptr->in = 0;
    shm_ptr->out = 0;
    shm_ptr->count = 0;
    shm_ptr->total_produced = 0;
    shm_ptr->total_consumed = 0;
    
    // TODO: 创建三个信号量(empty_sem, full_sem, mutex_sem)
    // 提示: 使用semget系统调用创建信号量
    
    
    // TODO: 初始化信号量的值
    // 提示: 使用semctl系统调用设置各信号量的初始值
    // empty_sem初始值应为BUFFER_SIZE, full_sem初始值为0, mutex_sem初始值为1
   // 创建信号量
    empty_sem = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
    full_sem = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
    mutex_sem = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
    
    // 初始化信号量
    semctl(empty_sem, 0, SETVAL, BUFFER_SIZE);  // 缓冲区初始为空
    semctl(full_sem, 0, SETVAL, 0);             // 缓冲区初始无产品
    semctl(mutex_sem, 0, SETVAL, 1);           // 互斥信号量初始为1 
}

// 清理信号量和共享内存
void cleanup_synchronization() {
    // TODO: 删除信号量
    // 提示: 使用semctl系统调用和IPC_RMID命令删除信号量
    // 删除信号量
    semctl(empty_sem, 0, IPC_RMID);
    semctl(full_sem, 0, IPC_RMID);
    semctl(mutex_sem, 0, IPC_RMID);
    
    // 分离共享内存
    if (shmdt(shm_ptr) == -1) {
        perror("shmdt 失败");
    }
    
    // 删除共享内存
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl 删除共享内存失败");
    }
}

// 打印缓冲区状态的函数
void print_buffer_status() {
    int i;
    
    printf("缓冲区: [");
    
    for (i = 0; i < BUFFER_SIZE; i++) {
        // 显示指针位置标记
        if (i == shm_ptr->in && i == shm_ptr->out) {
            printf("IO");
        } else if (i == shm_ptr->in) {
            printf(" I");
        } else if (i == shm_ptr->out) {
            printf(" O");
        } else {
            printf("  ");
        }
        
        // 直接显示当前位置的缓冲区内容
        if ((shm_ptr->out <= shm_ptr->in && (i >= shm_ptr->out && i < shm_ptr->in)) || 
            (shm_ptr->out > shm_ptr->in && (i >= shm_ptr->out || i < shm_ptr->in))) {
            printf("%3d ", shm_ptr->buffer[i]);
            // 有效缓冲区范围内
        } else {
            // 有效缓冲区范围内
            printf("... ");
        }
    }
    
    printf("] 使用率: %d/%d\n", shm_ptr->count, BUFFER_SIZE);
}

// 生产者进程函数
void producer_process(int id) {
    int item;
    
    printf("生产者进程 #%d (PID=%d) 开始运行\n", id, getpid());
    
    // 重新设置随机数种子，确保各进程生成不同的随机数
    srand(time(NULL) ^ (getpid()<<16));
    
    // TODO: 在此进程中找到合适位置添加信号量操作，实现生产者-消费者模式的同步控制
    // 提示: 
    // 1. 生产者需要等待空位，获取互斥访问，添加产品后释放互斥访问并增加满位
    // 2. 使用sem_p和sem_v函数分别实现P操作(wait)和V操作(signal)
    // 3. 需要操作的信号量有: empty_sem, mutex_sem, full_sem
    
    for (int i = 0; i < ITEMS_PER_PRODUCER; i++) {
	// 等待空位
        sem_p(empty_sem);
        
        // 获取互斥访问
        sem_p(mutex_sem);

        // 生产一个产品 (使用生产者ID作为特征)
        item = (id * 100) + (i + 1);
        
        // 临界区 - 放入产品到缓冲区
        shm_ptr->buffer[shm_ptr->in] = item;
        shm_ptr->in = (shm_ptr->in + 1) % BUFFER_SIZE;
        shm_ptr->count++;
        printf("生产者 #%d (PID=%d): 在位置 %d 放入产品 %d [总计: %d]\n", 
               id, getpid(), (shm_ptr->in == 0) ? BUFFER_SIZE - 1 : shm_ptr->in - 1, 
               item, ++shm_ptr->total_produced);
	// 释放互斥访问
        sem_v(mutex_sem);
        
        // 增加满位
        sem_v(full_sem);
        
        // 打印缓冲区状态
        print_buffer_status();
        
        // 随机休眠一段时间
        usleep(rand() % 500000);
    }
    
    printf("生产者 #%d (PID=%d): 已完成所有生产任务\n", id, getpid());
}

// 消费者进程函数
void consumer_process(int id) {
    int item;
    
    printf("消费者进程 #%d (PID=%d) 开始运行\n", id, getpid());
    
    // 重新设置随机数种子，确保各进程生成不同的随机数
    srand(time(NULL) ^ (getpid()<<16));
    
    // TODO: 在此进程中找到合适位置添加信号量操作，实现生产者-消费者模式的同步控制
    // 提示: 
    // 1. 消费者需要等待满位，获取互斥访问，取出产品后释放互斥访问并增加空位
    // 2. 使用sem_p和sem_v函数分别实现P操作(wait)和V操作(signal)
    // 3. 需要操作的信号量有: full_sem, mutex_sem, empty_sem
    
    for (int i = 0; i < ITEMS_PER_CONSUMER; i++) {
	// 等待满位
        sem_p(full_sem);
        
        // 获取互斥访问
        sem_p(mutex_sem);

        // 临界区 - 从缓冲区取出产品
        item = shm_ptr->buffer[shm_ptr->out];
        shm_ptr->out = (shm_ptr->out + 1) % BUFFER_SIZE;
        shm_ptr->count--;
        printf("消费者 #%d (PID=%d): 从位置 %d 取出产品 %d [总计: %d]\n", 
               id, getpid(), (shm_ptr->out == 0) ? BUFFER_SIZE - 1 : shm_ptr->out - 1, 
               item, ++shm_ptr->total_consumed);
       	// 释放互斥访问
        sem_v(mutex_sem);
        
        // 增加空位
        sem_v(empty_sem);
 
        // 打印缓冲区状态
        print_buffer_status();
        
        // 随机休眠一段时间
        usleep(rand() % 800000);
    }
    
    printf("消费者 #%d (PID=%d): 已完成所有消费任务\n", id, getpid());
}
